﻿using Microsoft.Practices.EnterpriseLibrary.Common.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging;
using Microsoft.Practices.EnterpriseLibrary.Logging.Configuration;
using Microsoft.Practices.EnterpriseLibrary.Logging.TraceListeners;
using Sidvall.Serialization;
using System.Collections.Generic;
using System.Diagnostics;

namespace Sidvall.LogManager.EL
{
    [ConfigurationElementType(typeof(CustomTraceListenerData))]
    public class EntityTraceListener : CustomTraceListener
    {
        #region Public Members

        #region LogScopeId

        public int? LogScopeId
        {
            get
            {
                if (this.Attributes.ContainsKey("LogScopeId"))
                    return this.Attributes["LogScopeId"].ToNullableInt32();
                return null;
            }
        }

        #endregion
        #region ConnectionString

        public string ConnectionString
        {
            get
            {
                if (this.Attributes.ContainsKey("ConnectionString"))
                    return this.Attributes["ConnectionString"];
                return null;
            }
        }

        #endregion

        #region GetSupportedAttributes

        protected override string[] GetSupportedAttributes()
        {
            var items = new List<string>();
            items.Add("LogScopeId");
            items.Add("ConnectionString");
            return items.ToArray();
        }

        #endregion
        #region TraceData

        public override void TraceData(TraceEventCache eventCache, string source, TraceEventType eventType, int id, object data)
        {
            LogEntry logEntry;
            Sidvall.Logging.EventType type;

            if ((this.Filter == null) || this.Filter.ShouldTrace(eventCache, source, eventType, id, null, null, data, null))
            {
                logEntry = data as LogEntry;
                if (logEntry != null)
                {
                    type = GetEventType(logEntry.Severity);
                    Write(this.LogScopeId.Value, logEntry.Title, logEntry.Message, logEntry.Categories, type, GetProperties(logEntry.ExtendedProperties));
                }
                else if (data is string)
                {
                    Write(data);
                }
                else
                {
                    base.TraceData(eventCache, source, eventType, id, data);
                }
            }
        }

        #endregion
        #region Write

        public override void Write(string message)
        {
            Write(this.LogScopeId.Value, string.Empty, message, null, Sidvall.Logging.EventType.Information, null);
        }

        #endregion
        #region WriteLine

        public override void WriteLine(string message)
        {
            Write(message);
        }

        #endregion

        #endregion
        #region Private Members

        private static readonly object _mutex = new object();

        #region GetEventType

        private static Sidvall.Logging.EventType GetEventType(System.Diagnostics.TraceEventType severity)
        {
            switch (severity)
            {
                case TraceEventType.Critical:
                    return Logging.EventType.Critical;
                case TraceEventType.Error:
                    return Logging.EventType.Error;
                case TraceEventType.Information:
                    return Logging.EventType.Information;
                case TraceEventType.Warning:
                    return Logging.EventType.Warning;
                default:
                    return Logging.EventType.Information;
            }
        }

        #endregion
        #region GetProperties

        private IDictionary<string, string> GetProperties(IDictionary<string, object> properties)
        {
            if (properties != null)
            {
                var items = new Dictionary<string, string>();
                foreach (var property in properties)
                    items.Add(property.Key, property.Value.ToString(null));
                return items;
            }
            return null;
        }

        #endregion
        #region Write

        private async void Write(int logScopeId, string title, string message, IEnumerable<string> categories, Sidvall.Logging.EventType severity, IDictionary<string, string> properties)
        {
            if (Sidvall.LogManager.SystemContext.Current.DataPortalStrategyFacade.ServerConnections.Count == 0)
            {
                lock (_mutex)
                {
                    if (Sidvall.LogManager.SystemContext.Current.DataPortalStrategyFacade.ServerConnections.Count == 0)
                    {
                        var item = new Net.Services.V1.ServerConnection()
                        {
                            ConnectionTypeId = Data.ConnectionType.NetworkSqlServer,
                            ConnectionString = this.ConnectionString,
                            IsEnabled = true,
                        };
                        Sidvall.LogManager.SystemContext.Current.AddServerConnection(item);
                    }
                }
            }
            await Sidvall.LogManager.Log.WriteAsync(logScopeId, title, message, categories, severity, properties);
        }

        #endregion

        #endregion
        #region Constructors

        public EntityTraceListener()
        {
        }

        #endregion
    }
}
